/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/12 10:55
  *
  * @file       pd_gpio.c
  * @brief      GPIO peripheral driver.
  *
  * @note       This file contains the peripheral driver for the GPIO.
  *
  ******************************************************************************
  * Change Logs:
  *   Date           Author       Notes
  *   2024-06-12     Chris        the first version
  *
  ******************************************************************************
  */

#include <stm32f4xx_ll_gpio.h>
#include <stm32f4xx_ll_bus.h>
#include "pd_gpio.h"

static void GPIO_init(const GPIO *this) {
    LL_GPIO_InitTypeDef GPIO_InitStruct;

    if (this->GPIOx == GPIOA) {
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
    } else if (this->GPIOx == GPIOB) {
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
    } else if (this->GPIOx == GPIOC) {
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
    } else if (this->GPIOx == GPIOD) {
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD);
    } else if (this->GPIOx == GPIOE) {
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOE);
    } else if (this->GPIOx == GPIOF) {
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOF);
    }

    GPIO_InitStruct.Pin        = this->Pin;
    GPIO_InitStruct.Mode       = this->Mode;
    GPIO_InitStruct.Speed      = this->Speed;
    GPIO_InitStruct.OutputType = this->OutputType;
    GPIO_InitStruct.Pull       = this->Pull;
    GPIO_InitStruct.Alternate  = this->Alternate;
    LL_GPIO_Init(this->GPIOx, &GPIO_InitStruct);
}

static void GPIO_setBit(const GPIO *this) {
    this->GPIOx->BSRR = this->Pin;
}

static void GPIO_resetBit(const GPIO *this) {
    this->GPIOx->BSRR = this->Pin << 16;
}


static void GPIO_setOutMode(const GPIO *this) {
    LL_GPIO_SetPinMode(this->GPIOx, this->Pin, LL_GPIO_MODE_OUTPUT);
}


static void GPIO_setInMode(const GPIO *this) {
    LL_GPIO_SetPinMode(this->GPIOx, this->Pin, LL_GPIO_MODE_INPUT);
}


static uint8_t GPIO_getBit(const GPIO *this) {
    return (this->GPIOx->IDR & this->Pin) == 0 ? 0 : 1;
}

static GPIO *build(GPIO_TypeDef *GPIOx, uint32_t Pin) {
    GPIO *gpio = malloc(sizeof(GPIO));

    Driver_add(gpio, GEN_PERM);

    gpio->GPIOx      = GPIOx,
    gpio->Pin        = Pin;

    gpio->Pull       = LL_GPIO_PULL_UP;
    gpio->Mode       = LL_GPIO_MODE_OUTPUT;
    gpio->Speed      = LL_GPIO_SPEED_FREQ_HIGH;
    gpio->OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    gpio->Alternate  = LL_GPIO_AF_0;

    gpio->init       = &GPIO_init;
    gpio->setBit     = &GPIO_setBit;
    gpio->resetBit   = &GPIO_resetBit;
    gpio->setOutMode = &GPIO_setOutMode;
    gpio->setInMode  = &GPIO_setInMode;
    gpio->getBit     = &GPIO_getBit;

    return gpio;
}

const struct GPIOClass GPIOBuilder = {.build = &build};

static GPIOList *GPIOList_build(GPIO **gpios, uint8_t gpioSize) {
    GPIOList *gpioList = malloc(sizeof(GPIOList));

    Driver_add(gpioList, GEN_PERM);

    gpioList->gpios    = gpios;
    gpioList->gpioSize = gpioSize;

    return gpioList;
}

const struct GPIOListClass GPIOListBuilder = {.build = &GPIOList_build};
